<?php

namespace app\common;

use app\common\Util;
use app\model\Menu;

//构建curd
class CurdBuild
{
    // 强制覆盖,默认false
    protected static $is_cover = false;

    public static function build(array $data): mixed
    {
        if ($data["is_cover"] == "true") {
            self::$is_cover = true;
        }

        $app = request()->app; //应用
        $table_name = $data["table_name"]; //菜单名称

        //创建模块
        $model_name = Util::nameToClass($data["table"]);
        $model_str = Util::guessPath(app_path(), 'model') ?: 'model';
        $model_file = app_path() . "/$model_str/{$model_name}.php";
        $model_namespace = $model_str === 'Model' ? 'App\Model' : 'app\model';
        $CreateModel = true;
        if (!file_exists($model_file) || (file_exists($model_file) && self::$is_cover)) {
            $CreateModel = self::CreateModel($model_name, $model_namespace, $model_file);
        }
        if (!$CreateModel) {
            return error("模块新建失败");
        }

        //创建验证器
        $validate_name = Util::nameToClass($data["table"]) . "Validate";
        $validate_file = app_path() . "/" . $app . "/validate/{$validate_name}.php";
        $validate_namespace =   'app' . '\\' . $app . '\validate';
        $createValidate = true;
        if (!file_exists($validate_file) || (file_exists($validate_file) && self::$is_cover)) {
            $CreateModel = self::createValidate($validate_name, $validate_namespace, $validate_file);
        }
        if (!$createValidate) {
            return error("验证器新建失败");
        }

        //创建控制器
        $controller_name = Util::nameToClass($data["table"]) . config('app.controller_suffix', '');
        $controller_file = app_path() . "/" . $app . "/controller/{$controller_name}.php";
        $controller_namespace = 'app' . '\\' . $app . '\controller';
        $createController = true;
        if (!file_exists($controller_file) || (file_exists($controller_file) && self::$is_cover)) {
            $CreateModel = self::createController(
                $controller_name,
                $controller_namespace,
                $table_name,
                $model_namespace . '\\' . $model_name,
                $model_name,
                $controller_file
            );
        }
        if (!$createController) {
            return error("控制器新建失败");
        }

        //生成视图
        $view_path = app_path() . "/" . $app . "/view" . '/' . Util::classToName($model_name) . '/';
        return self::createView($model_name, $view_path, $table_name);
    }

    /**
     * 创建视图 function
     *
     * @param string $class 类名
     * @param string $file_name 视图文件夹名
     * @param string $title 视图名称
     * @return mixed
     */
    protected static function createView(string $class, string $file_name, string $title): mixed
    {
        $name = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $class));
        $viewQueryFile = $file_name . "query.html";
        $prefix = config('thinkorm.connections.mysql.prefix') ?? ''; //表前缀
        $database = config('thinkorm.connections.mysql.database'); //数据库
        $fileds = [];
        foreach (\think\facade\Db::query("select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,COLUMN_COMMENT from INFORMATION_SCHEMA.COLUMNS where table_name = '$prefix$name' and table_schema = '$database' ORDER BY ORDINAL_POSITION ASC") as $item) {
            $key = false;
            if ($item['COLUMN_KEY'] === 'PRI') {
                $key = true;
            }
            $fileds[] = ["title" => $item['COLUMN_COMMENT'], "field" => $item['COLUMN_NAME'], "key" => $key];
        }
        $firstChar = strtolower(substr($class, 0, 1));
        $restOfString = substr($class, 1);
        $ViewName = $firstChar . $restOfString;
        //视图查询
        $createViewQuery = true;
        if (!file_exists($viewQueryFile) || (file_exists($viewQueryFile) && self::$is_cover)) {
            $createViewQuery = self::createViewQuery($ViewName, $fileds, $title, $viewQueryFile);
        }
        if (!$createViewQuery) {
            return error("视图查询新建失败");
        }
        //视图添加
        $createViewAdd = true;
        $viewAddFile = $file_name . "add.html";
        if (!file_exists($viewAddFile) || (file_exists($viewAddFile) && self::$is_cover)) {
            $createViewAdd = self::createViewAdd($viewAddFile, $fileds);
        }
        if (!$createViewAdd) {
            return error("视图添加新建失败");
        }
        //视图编辑
        $createViewEdit = true;
        $viewEditFile = $file_name . "edit.html";
        if (!file_exists($viewEditFile) || (file_exists($viewEditFile) && self::$is_cover)) {
            $createViewEdit = self::createViewEdit($viewEditFile, $fileds);
        }
        if (!$createViewEdit) {
            return error("视图编辑新建失败");
        }
        //视图回收站
        $createViewRecycled = true;
        $viewRecycledFile = $file_name . "recycled.html";
        if (!file_exists($viewRecycledFile) || (file_exists($viewRecycledFile) && self::$is_cover)) {
            $createViewRecycled = self::createViewRecycled($ViewName, $fileds, $viewRecycledFile);
        }
        if (!$createViewRecycled) {
            return error("视图回收站新建失败");
        }

        return success("操作成功");
    }

    /**
     * 创建回收站视图 function
     *
     * @param string $name 名
     * @param array $fields 字段组
     * @param string $file 文件
     * @return boolean
     */
    protected static function createViewRecycled(string $name, array $fields, string $file): bool
    {
        $path = pathinfo($file, PATHINFO_DIRNAME);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $filed_str = "";
        foreach ($fields as $k => $v) {
            $hide = "";
            if (!in_array($v["field"], ["id", "delete_time"])) {
                $hide = ", hide: true";
            }
            $filed_str .= "                { title: '" . $v["title"] . "', field: '" . $v["field"] . "', align: 'center', sort: true $hide },\n";
        }
        $filed_str = trim($filed_str);
        $content_str = <<<EOF
        {include file="public/header" /}
        <blockquote class="layui-elem-quote">
            回收站
        </blockquote>
        <!-- 表单信息 -->
        <table class="layui-hide" id="table" lay-filter="table"></table>
        {include file="public/footer" /}
        <script>
            layui.use(["table", "tableBar", "dropdown", "jquery", "popup"], function () {
                var table = layui.table;
                var dropdown = layui.dropdown;
                var tableBar = layui.tableBar;
                var popup = layui.popup;
                var $ = layui.$;

                // 表单
                table.render({
                    elem: "#table",
                    url: "{:route('admin.$name.recycled')}",
                    toolbar: `<div>
                            <button type="button" class="layui-btn layui-btn-xs" kllxs-on="kl-restore">批量还原</button>
                            <button type="button" class="layui-btn layui-bg-red layui-btn-xs" kllxs-on="kl-clean">批量清除</button>
                        </div>`,
                    cols: [[
                        { fixed: 'left', type: 'checkbox', align: 'center', width: 80 },
                        $filed_str
                        { title: "操作", minWidth: 100, fixed: 'right', align: 'center', templet: tableBar.more() }
                    ]],
                    method: "post",
                    limits: [10, 25, 50, 100],
                    autoSort: false, // 禁用前端自动排序。
                    page: true
                })
                $('body').delegate('[kllxs-on="kl-restore"]', 'click', function () {
                    var checkStatus = table.checkStatus("table")
                    var data = checkStatus.data; // 获取选中的数据
                    if (data.length !== 0) {
                        var id = [];
                        $.each(data, function (k, v) {
                            id.push(v.id)
                        })
                        popup.confirm(`{:route("admin.$name.restore")}`, { id: id }, "POST")
                    } else {
                        popup.msg({ code: 0, msg: "请选择数据", fun: function () { } })
                    }
                })

                $('body').delegate('[kllxs-on="kl-clean"]', 'click', function () {
                    var checkStatus = table.checkStatus("table")
                    var data = checkStatus.data; // 获取选中的数据
                    if (data.length !== 0) {
                        var id = [];
                        $.each(data, function (k, v) {
                            id.push(v.id)
                        })
                        popup.confirm(`{:route("admin.$name.clean")}`, { id: id })
                    } else {
                        popup.msg({ code: 0, msg: "请选择数据", fun: function () { } })
                    }
                })
                tableBar.util({}, {
                    "kl-more": function () {
                        var info = JSON.parse($(this).attr("data-info"))
                        dropdown.render({
                            elem: this, // 绑定元素选择器，此处指向 class 可同时绑定多个元素
                            show: true,
                            data: [{
                                title: '还原',
                                id: "restore"
                            }, {
                                title: '清理',
                                id: "clean"
                            }],
                            click: function (obj) {
                                if (obj.id === "restore") {
                                    popup.confirm(`{:route("admin.$name.restore")}`, { id: info.id }, "POST")
                                } else {
                                    popup.confirm(`{:route("admin.$name.clean")}`, { id: info.id })
                                }
                            }
                        });
                    }
                });
            })
        </script>

        EOF;
        return file_put_contents($file, $content_str);
    }

    /**
     * 创建编辑视图 function
     *
     * @param string $file
     * @param array $fields
     * @return boolean
     */
    protected static function createViewEdit(string $file, array $fields): bool
    {
        $path = pathinfo($file, PATHINFO_DIRNAME);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $html = "";
        foreach ($fields as $k => $v) {
            if (in_array($v["field"], ["create_time", "update_time", "delete_time"]) || $v["key"]) {
                continue;
            }
            $html .=
                "    <div class=\"layui-form-item\">
        <label class=\"layui-form-label\">" . $v["title"] . "</label>
        <div class=\"layui-input-block\">
            <input type=\"text\" name=\"" . $v["field"] . "\" autocomplete=\"off\" placeholder=\"请输入" . $v["title"] . "\" class=\"layui-input\" value=\"{\$find." . $v["field"] . "}\">
        </div>
    </div>\n";
        }
        $html = trim($html);
        $content_str = <<<EOF
        {include file="public/header" /}
        <blockquote class="layui-elem-quote">
            编辑
        </blockquote>
        <form class="layui-form layui-form-pane kllxs-form" method="post">
            $html
            <div class="layui-form-item">
                <button class="layui-btn" lay-submit lay-filter="kllxs-submit">确认</button>
                <button type="reset" class="layui-btn layui-btn-primary">重置</button>
            </div>
        </form>
        {include file="public/footer" /}
        <script>
            layui.use(["form", "popup"], function () {
                var form = layui.form;
                var popup = layui.popup;
        
                form.on('submit(kllxs-submit)', function (data) {
                    var field = data.field; // 获取表单全部字段值
        
                    popup.formSubmit("", field)
        
                    return false; // 阻止默认 form 跳转
                })
            })
        </script>
        EOF;
        return file_put_contents($file, $content_str);
    }

    /**
     * 创建添加视图 function
     *
     * @param string $file
     * @param array $fields
     * @return boolean
     */
    protected static function createViewAdd(string $file, array $fields): bool
    {
        $path = pathinfo($file, PATHINFO_DIRNAME);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $html = "";
        foreach ($fields as $k => $v) {
            if (in_array($v["field"], ["create_time", "update_time", "delete_time"]) || $v["key"]) {
                continue;
            }
            $html .=
                "    <div class=\"layui-form-item\">
        <label class=\"layui-form-label\">" . $v["title"] . "</label>
        <div class=\"layui-input-block\">
            <input type=\"text\" name=\"" . $v["field"] . "\" autocomplete=\"off\" placeholder=\"请输入" . $v["title"] . "\" class=\"layui-input\" value=\"\">
        </div>
    </div>\n";
        }
        $html = trim($html);
        $content_str = <<<EOF
        {include file="public/header" /}
        <blockquote class="layui-elem-quote">
            添加
        </blockquote>
        <form class="layui-form layui-form-pane kllxs-form" method="post">
            $html
            <div class="layui-form-item">
                <button class="layui-btn" lay-submit lay-filter="kllxs-submit">确认</button>
                <button type="reset" class="layui-btn layui-btn-primary">重置</button>
            </div>
        </form>
        {include file="public/footer" /}
        <script>
            layui.use(["form", "popup"], function () {
                var form = layui.form;
                var popup = layui.popup;
        
                form.on('submit(kllxs-submit)', function (data) {
                    var field = data.field; // 获取表单全部字段值
        
                    popup.formSubmit("", field)
        
                    return false; // 阻止默认 form 跳转
                })
            })
        </script>
        EOF;
        return file_put_contents($file, $content_str);
    }

    /**
     * 创建查询视图 function
     *
     * @param string $name 名
     * @param array $fields 字段组
     * @param string $title 标题
     * @param string $file 路径
     * @return boolean
     */
    protected static function createViewQuery(string $name, array $fields, string $title, string $file): bool
    {
        $path = pathinfo($file, PATHINFO_DIRNAME);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $filed_str = "";
        foreach ($fields as $k => $v) {
            if ($v["field"] == "delete_time") {
                continue;
            }
            $hide = "";
            if (in_array($v["field"], ["create_time", "update_time"])) {
                $hide = ", hide: true";
            }
            $filed_str .= "                { title: '" . $v["title"] . "', field: '" . $v["field"] . "', align: 'center', search: true , sort: true $hide },\n";
        }
        $filed_str = trim($filed_str);
        $content_str = <<<EOF
        {include file="public/header" /}
        <blockquote class="layui-elem-quote">
            $title
        </blockquote>
        <!-- 搜索信息 -->
        <div id="kl-search-form" lay-filter="kl-search-form"></div>
        <!-- 表单信息 -->
        <table class="layui-hide" id="table" lay-filter="table"></table>
        {include file="public/footer" /}
        <script>
            layui.use(["table", "tableBar"], function () {
                var table = layui.table;
                var tableBar = layui.tableBar;
        
                // 表单
                table.render({
                    elem: "#table",
                    url: "{:route('admin.$name.query')}",
                    toolbar: tableBar.toolbar(),
                    defaultToolbar: tableBar.defaultToolbar(),
                    cols: [[
                        { fixed: 'left', type: 'checkbox', align: 'center', width: 80 },
                        $filed_str
                        { title: "操作", minWidth: 100, fixed: 'right', align: 'center', templet: tableBar.more() }
                    ]],
                    method: "post",
                    limits: [10, 25, 50, 100],
                    autoSort: false, // 禁用前端自动排序。
                    page: true,
                    before: function () { tableBar.tableSearch(this.cols) }, //渲染前回调
                    done: tableBar.tableDone
                })
        
                tableBar.util({
                    add: '{:route("admin.$name.add")}', //添加链接
                    edit: '{:route("admin.$name.edit")}', //编辑链接
                    del: '{:route("admin.$name.del")}', //删除
                    recycled: '{:route("admin.$name.recycled")}', //回收站
                });
            })
        </script>

        EOF;
        return file_put_contents($file, $content_str);
    }

    /**
     * 创建验证器 function
     *
     * @param string $name 类名
     * @param string $namespace 命名
     * @param string $file 文件
     * @return boolean
     */
    protected static function createValidate(
        string $name,
        string $namespace,
        string $file
    ): bool {
        $path = pathinfo($file, PATHINFO_DIRNAME);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $validate_content = <<<EOF
<?php

namespace $namespace;

use app\common\Validate;

class $name extends Validate
{
    /**
     * 验证规则
     */
    protected \$rule = [

    ];
    /**
     * 提示消息
     */
    protected \$message = [

    ];
    /**
     * 验证场景
     */
    protected \$scene = [
        'add'  => [],
        'edit' => [],
    ];

}

EOF;
        return file_put_contents($file, $validate_content);
    }

    /**
     * 创建控制器 function
     *
     * @param string $name 类名
     * @param string $namespace 命名
     * @param string $title 标题
     * @param string $model_namespace use模块
     * @param string $model_name 模块名
     * @param string $file 文件路径
     * @return boolean
     */
    protected static function createController(
        string $name,
        string $namespace,
        string $title,
        string $model_namespace,
        string $model_name,
        string $file
    ): bool {
        $path = pathinfo($file, PATHINFO_DIRNAME);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $controller_content = <<<EOF
<?php

namespace $namespace;

use support\Request;
use app\common\Attribute;
use app\common\View;
use $model_namespace;

#[Attribute(title: "$title")]
class $name
{
    #[Attribute(isAuth: true, isPage: true, title: "查询")]
    public function query(Request \$request)
    {
        if (\$request->method() == "POST") {
            \$all = \$request->all();
            \$order = ["id" => "desc"];
            if (isset(\$all["order"]) && \$all["order"] != null) {
                \$order = [\$all["order"]["field"] => \$all["order"]["order"]];
            }
            \$data = $model_name::where(function (\$data) use (\$all) {
                if (isset(\$all["where"])) {
                    \$w = [];
                    foreach (\$all["where"] as \$k => \$v) {
                        if (\$v != "" && \$v != null) {
                            \$w[] = [\$k, "like", '%' . \$v . '%'];
                        } else {
                            continue;
                        }
                    }
                    if (count(\$w) > 0) {
                        \$data->where(\$w);
                    }
                }
            })
                ->order(\$order)
                ->paginate([
                    "list_rows" => \$all["limit"],
                    "page" => \$all["page"]
                ])->toArray();
            return json([
                "code" => 0,
                "msg" => "获取成功",
                "count" => \$data["total"],
                "data" => \$data["data"],
            ]);
        }
        return View::fetch();
    }

    #[Attribute(isAuth: true, isPage: true, title: "添加")]
    public function add(Request \$request)
    {
        if (\$request->method() == "POST") {
            \$all = \$request->all();
            try {
                validate("$model_name", 'add', \$all);
                \$res = $model_name::create(\$all);
                if (\$res) {
                    return success("操作成功");
                } else {
                    return error("操作失败");
                }
            } catch (\\think\\exception\\ValidateException \$e) {
                return error(msg: "验证失败", data: \$e->getError());
            }
        }
        return View::fetch();
    }

    #[Attribute(isAuth: true, isPage: false, title: "编辑")]
    public function edit(Request \$request)
    {
        if (\$request->method() == "GET") {
            \$id = \$request->get('id');
            \$find = $model_name::find(\$id);
            View::assign('find', \$find);
        }
        if (\$request->method() == "POST") {
            \$all = \$request->all();
            try {
                validate("$model_name", 'edit', \$all);
                \$res = $model_name::update(\$all);
                if (\$res) {
                    return success("操作成功");
                } else {
                    return error("操作失败");
                }
            } catch (\\think\\exception\\ValidateException \$e) {
                return error(msg: "验证失败", data: \$e->getError());
            }
        }
        return View::fetch();
    }

    #[Attribute(isAuth: true, isPage: false, title: "删除")]
    public function del(Request \$request)
    {
        if (\$request->method() == "DELETE") {
            \$all = \$request->all();
            \$res = $model_name::destroy(\$all['id']);
            if (\$res) {
                return success("操作成功");
            } else {
                return error("操作失败");
            }
        }
    }

    #[Attribute(isAuth: true, isPage: true, title: "回收站")]
    public function recycled(Request \$request)
    {
        if (\$request->method() == "POST") {
            \$all = \$request->all();
            \$order = ["id" => "desc"];
            \$data = $model_name::onlyTrashed()
                ->order(\$order)
                ->page(\$all["page"], \$all["limit"])
                ->select();
            return json([
                "code" => 0,
                "msg" => "获取成功",
                "count" => count(\$data),
                "data" => \$data->toArray(),
            ]);
        }
        return View::fetch();
    }

    #[Attribute(isAuth: true, isPage: false, title: "还原")]
    public function restore(Request \$request)
    {
        if (\$request->method() == "POST") {
            \$all = \$request->all();
            \$res = $model_name::onlyTrashed()
                ->whereIn('id', \$all["id"])
                ->select()
                ->each(function (\$item) {
                    \$item->restore();
                });
            if (\$res) {
                return success("操作成功");
            } else {
                return error("操作失败");
            }
        }
    }

    #[Attribute(isAuth: true, isPage: false, title: "清理")]
    public function clean(Request \$request)
    {
        if (\$request->method() == "DELETE") {
            \$all = \$request->all();
            \$res = $model_name::destroy(\$all['id'], true);
            if (\$res) {
                return success("操作成功");
            } else {
                return error("操作失败");
            }
        }
    }
}

EOF;
        return file_put_contents($file, $controller_content);
    }

    /**
     * 创建模块 function
     *
     * @param string $name 表名
     * @param string $namespace 命名空间
     * @param string $file 文件路径
     * @return boolean
     */
    protected static function CreateModel(string $name, string $namespace, string $file): bool
    {
        $path = pathinfo($file, PATHINFO_DIRNAME);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $table = Util::classToName($name);
        $table_val = 'null';
        $pk = 'id';
        $properties = '';
        try {
            $prefix = config('thinkorm.connections.mysql.prefix') ?? '';
            $database = config('thinkorm.connections.mysql.database');
            if (\think\facade\Db::query("show tables like '{$prefix}{$table}'")) {
                $table = "{$prefix}{$table}";
                $table_val = "'$table'";
            } else if (\think\facade\Db::query("show tables like '{$prefix}{$table}s'")) {
                $table = "{$prefix}{$table}s";
                $table_val = "'$table'";
            }
            foreach (\think\facade\Db::query("select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,COLUMN_COMMENT from INFORMATION_SCHEMA.COLUMNS where table_name = '$table' and table_schema = '$database'") as $item) {
                if ($item['COLUMN_KEY'] === 'PRI') {
                    $pk = $item['COLUMN_NAME'];
                    $item['COLUMN_COMMENT'] .= "(主键)";
                }
                $type = self::getType($item['DATA_TYPE']);
                $properties .= " * @property $type \${$item['COLUMN_NAME']} {$item['COLUMN_COMMENT']}\n";
            }
        } catch (\Throwable $e) {
        }
        $properties = rtrim($properties) ?: ' *';
        $model_content = <<<EOF
<?php

namespace $namespace;

use think\Model;
use think\model\concern\SoftDelete;

/**
$properties
 */
class $name extends Model
{
    use SoftDelete;
    protected \$deleteTime = 'delete_time';
    protected \$defaultSoftDelete = 0;
    protected \$type = [
        "delete_time" => 'timestamp'
    ];

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected \$table = $table_val;

    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected \$pk = '$pk';

    
}

EOF;
        return file_put_contents($file, $model_content);
    }


    /**
     * @param string $type
     * @return string
     */
    protected static function getType(string $type)
    {
        if (strpos($type, 'int') !== false) {
            return 'integer';
        }
        switch ($type) {
            case 'varchar':
            case 'string':
            case 'text':
            case 'date':
            case 'time':
            case 'guid':
            case 'datetimetz':
            case 'datetime':
            case 'decimal':
            case 'enum':
                return 'string';
            case 'boolean':
                return 'integer';
            case 'float':
                return 'float';
            default:
                return 'mixed';
        }
    }
}
